package com.qixiaobao.common.utils.ocr;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.qixiaobao.common.constant.BaiduOcrConstant;
import com.qixiaobao.common.constant.CacheConstants;
import com.qixiaobao.common.core.redis.RedisCache;
import com.qixiaobao.common.utils.Base64Util;
import com.qixiaobao.common.utils.DateUtils;
import com.qixiaobao.common.utils.StringUtils;
import com.qixiaobao.common.utils.file.FileUtils;
import com.qixiaobao.common.utils.http.HttpUtils;
import com.qixiaobao.common.utils.oss.OssUtil;
import com.qixiaobao.common.utils.uuid.IdUtils;
import org.apache.commons.codec.digest.DigestUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import java.math.BigDecimal;
import java.net.URLEncoder;
import java.text.ParseException;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * @Author yangxm
 * @Description 发票识别工具类
 */
@ConfigurationProperties(prefix = "ocr")
@Component
public class OcrUtil {

    private static final Logger log = LoggerFactory.getLogger("adminLogger");
    private static String apiKey;
    private static String secretKey;
    private static String invoiceUrl;
    private static String tokenUrl;
    private static String bankReceiptUrl;

    /** 支持的图片格式 */
    private static final List<String> IMAGE_TYPES = Arrays.asList("jpg","jpeg","png","bmp");
    /** pdf格式 */
    private static final String PDF_TYPE = "pdf";
    /** 图片 参数 */
    private static final String IMAGE = "image";
    /** pdf文件 参数 */
    private static final String PDF = "pdf_file";
    /** 返回字段：access_token */
    private static final String TOKEN = "access_token";
    /** 返回字段：error_code */
    private static final String ERROR = "error_code";

    @Autowired
    private RedisCache redisCache;

    /**
     * @Description 获取权限token
     * @Author yangxm
     * @Date 2023/5/31 10:33 AM
     * @Param []
     */
    public String getToken(){
        if (redisCache.hasKey(CacheConstants.OCR_TOKEN)){
            return redisCache.getCacheObject(CacheConstants.OCR_TOKEN);
        }
        String getAccessTokenUrl = tokenUrl
                // 1. grant_type为固定参数
                + "grant_type=client_credentials"
                // 2. 官网获取的 API Key
                + "&client_id=" + apiKey
                // 3. 官网获取的 Secret Key
                + "&client_secret=" + secretKey;

        String result = HttpUtils.sendGet(getAccessTokenUrl);
        if (StringUtils.isNotBlank(result)){
            JSONObject jsonObject = JSON.parseObject(result);
            String accessToken = jsonObject.getString(TOKEN);
            log.info("获取百度token成功，access_token：{}", accessToken);
            //保存到redis中，缓存时间29天
            redisCache.setCacheObject(CacheConstants.OCR_TOKEN, accessToken, 29, TimeUnit.DAYS);
            return accessToken;
        }
        return null;
    }

    /**
     * @Description 发票文件解析
     * @Author yangxm
     * @Date 2023/5/31 4:29 PM
     * @Param [file]
     */
    public Map<String, Object> vatInVoice(MultipartFile[] files) throws ParseException {
        long begin = System.currentTimeMillis();
        log.info("发票文件解析，文件数量：{}-------------------------------", files.length);
        Map<String, Object> map = processFile(files, invoiceUrl);
        log.info("发票文件解析完成，耗时：{}", System.currentTimeMillis() - begin);
        return map;
    }

    /**
     * @Description 发票识别
     * @Author yangxm
     * @Date 2024/12/5 15:02
     * @param file
     * @return com.supplier.manage.server.common.utils.ocr.OcrFile
     */
    public OcrFile vatInVoice(MultipartFile file) throws Exception {
        long begin = System.currentTimeMillis();
        log.info("发票文件解析，文件名称：{}", file.getOriginalFilename());
        if (file.isEmpty()){
            throw new IllegalArgumentException("上传的文件不能为空");
        }
        //传递参数名
        String paramType="";
        //获取token
        String token = getToken();
        String errorMsg ="";
        OcrFile ocrInvoiceFile=null;
        String fileOrigName =file.getOriginalFilename();
        //文件后缀
        String fileSuffix=fileOrigName.substring(fileOrigName.lastIndexOf(".")+1);
        if (PDF_TYPE.equalsIgnoreCase(fileSuffix)){
            paramType = PDF;
        }else if (IMAGE_TYPES.contains(fileSuffix.toLowerCase())){
            paramType = IMAGE;
        }else {
            throw new IllegalArgumentException("文件格式错误，仅支持pdf/jpg/jpeg/png/bmp格式！");
        }
        //传递参数值
        String fileParam=URLEncoder.encode(Base64Util.encode(file.getBytes()), "UTF-8");
        //调用百度发票识别接口
        //发票识别返回字符串
        String result = HttpUtils.sendOcrPost(invoiceUrl, token, paramType + "=" + fileParam);
        log.info("发票解析结果：{}", result);
        //发票识别返回json
        JSONObject jsonResult = JSON.parseObject(result);
        errorMsg =Optional.ofNullable(jsonResult.getInteger(ERROR)).map(BaiduOcrConstant.ERROR_CODE_MAP::get).orElse("");
        if(StringUtils.isNotBlank(errorMsg)){
            throw new IllegalArgumentException(errorMsg);
        }
        //发票内容解析
        //发票内容json
        JSONObject wordsResult=jsonResult.getJSONObject("words_result");
        //封装实体类
        ocrInvoiceFile = new OcrFile();
        //发票识别调用
        //发票识别接口唯一ID
        ocrInvoiceFile.setLogId(jsonResult.getString("log_id"));
        //发票号码
        ocrInvoiceFile.setInvoiceNum( wordsResult.getString("InvoiceNum"));
        //开票日期
        ocrInvoiceFile.setInvoiceDate(wordsResult.getDate("InvoiceDate"));
        //发票金额
        ocrInvoiceFile.setTotalAmount(org.apache.commons.lang3.StringUtils.isBlank(wordsResult.getString("TotalAmount")) ? BigDecimal.ZERO : new BigDecimal(wordsResult.getString("TotalAmount")));
        //价税总额
        ocrInvoiceFile.setInvoiceMoney(Optional.ofNullable(wordsResult.getBigDecimal("AmountInFiguers")).orElse(BigDecimal.ZERO));
        //税率
        ocrInvoiceFile.setTaxRate(wordsResult.getJSONArray("CommodityTaxRate").getJSONObject(0).getString("word"));
        //上传到oss
        ocrInvoiceFile.setUrl(OssUtil.ins().uploadFile(file));
        log.info("发票文件解析完成，耗时：{}", System.currentTimeMillis() - begin);
        return ocrInvoiceFile;
    }

    /**
     * @Description 银行回执单识别
     * @Author yangxm
     * @Date 2023/6/1 1:23 PM
     * @Param [file]
     */
    public Map<String, Object> bankReceipt(MultipartFile[] files) throws Exception {
        return processFile(files, bankReceiptUrl);
    }

    /**
     * @Description 文件处理
     * @Author yangxm
     * @Date 2023/6/1 1:07 PM
     * @Param [file]
     */
    private Map<String, Object> processFile(MultipartFile[] files, String url) throws ParseException {
        if (files == null || files.length == 0){
            throw new IllegalArgumentException("上传的文件不能为空");
        }
        Map<String, Object> map = new HashMap<>();
        int successNum = 0;
        int failureNum = 0;
        StringBuilder successMsg = new StringBuilder();
        StringBuilder failureMsg = new StringBuilder();
        //文件名称带后缀
        String originalFilename;
        //文件后缀
        String fileSuffix;
        //文件名称，不带后缀
        String fileName;
        //传递参数名
        String paramType;
        //传递参数值
        String fileParam;
        //发票识别返回字符串
        String result;
        //发票识别返回json
        JSONObject jsonResult = new JSONObject();
        //发票内容json
        JSONObject wordsResult;
        //文件保存路径
        String filePath;
        //文件URL地址
        String fileUrl;
        //文件MD5值
        String fileMd5 = null;
        MultipartFile file;
        OcrFile ocrFile;
        List<OcrFile> list = new ArrayList<>();
        //获取token
        String token = getToken();
        if (StringUtils.isBlank(token)){
            throw new RuntimeException("获取百度云token失败");
        }
        for (int i = 0; i < files.length; i++) {
            file = files[i];
            originalFilename = file.getOriginalFilename();
            if (StringUtils.isBlank(originalFilename)){
                failureNum++;
                failureMsg.append("<br/>").append(failureNum).append("、文件名错误，请检查");
                continue;
            }
            //文件后缀，不带.
            fileSuffix = originalFilename.substring(originalFilename.lastIndexOf(".") + 1);
            if (StringUtils.isBlank(fileSuffix)){
                failureNum++;
                failureMsg.append("<br/>").append(failureNum).append("、文件名 ").append("缺少后缀，请检查");
                continue;
            }
            fileName = originalFilename.substring(0, originalFilename.lastIndexOf("."));
            if (PDF_TYPE.equalsIgnoreCase(fileSuffix)){
                paramType = PDF;
            }else if (IMAGE_TYPES.contains(fileSuffix.toLowerCase())){
                paramType = IMAGE;
            }else {
                failureNum++;
                failureMsg.append("<br/>").append(failureNum).append("、文件名 ").append(fileName).append(" 格式错误，仅支持pdf/jpg/jpeg/png/bmp格式！");
                continue;
            }
            //文件后缀，带.
            fileSuffix = originalFilename.substring(originalFilename.lastIndexOf("."));
            //上传路径
            filePath = FileUtils.getPath() + IdUtils.fastSimpleUUID() + fileSuffix;
            try {
                //文件MD5值
                fileMd5 = DigestUtils.md5Hex(file.getBytes());
                fileParam = URLEncoder.encode(Base64Util.encode(file.getBytes()), "UTF-8");
                //调用百度发票识别接口
                result = HttpUtils.sendOcrPost(url, token, paramType + "=" + fileParam);
                log.info("发票文件：{}, 发票识别的数据：{}", fileName, result);
                jsonResult = JSON.parseObject(result);
                if (jsonResult.getInteger(ERROR) != null){
                    String errorMsg = BaiduOcrConstant.ERROR_CODE_MAP.get(jsonResult.getInteger(ERROR));
                    failureNum++;
                    failureMsg.append("<br/>").append(failureNum).append("、文件名 ").append(fileName).append(" 识别失败，错误信息：").append(errorMsg);
                    continue;
                }
                //上传OSS系统
                OssUtil.ins().uploadFile(filePath, file.getInputStream());
            } catch (Exception e){
                failureNum++;
                failureMsg.append("<br/>").append(failureNum).append("、文件名 ").append(fileName).append(" 识别失败:").append(e.getMessage());
            }
            //获取url
            fileUrl = OssUtil.ins().getFileUrl(filePath);
            //发票内容解析
            wordsResult = jsonResult.getJSONObject("words_result");
            //封装实体类
            ocrFile = new OcrFile();
            //发票识别调用
            if (invoiceUrl.equals(url)){
                //file文件的MD5值
                ocrFile.setFileMd5(fileMd5);
                //文件名称
                ocrFile.setFileName(fileName);
                //发票识别接口唯一ID
                ocrFile.setLogId(jsonResult.getString("log_id"));
                // 发票号码
                ocrFile.setInvoiceNum( wordsResult.getString("InvoiceNum"));
                // 开票日期
                if (StringUtils.isNotBlank(wordsResult.getString("InvoiceDate"))){
                    ocrFile.setInvoiceDate(DateUtils.parseDate(DateUtils.chineseDateToIso(wordsResult.getString("InvoiceDate"))));
                }
                // 发票合计金额
                ocrFile.setTotalAmount(StringUtils.isBlank(wordsResult.getString("TotalAmount")) ? BigDecimal.ZERO : new BigDecimal(wordsResult.getString("TotalAmount")));
                //价税合计（小写）
                ocrFile.setInvoiceMoney(StringUtils.isBlank(wordsResult.getString("AmountInFiguers")) ? BigDecimal.ZERO : new BigDecimal(wordsResult.getString("AmountInFiguers")));
                //税率
                if (StringUtils.isNotEmpty(wordsResult.getJSONArray("CommodityTaxRate"))){
                    ocrFile.setTaxRate(wordsResult.getJSONArray("CommodityTaxRate").getJSONObject(0).getString("word"));
                }
                //实际付款单位（发票抬头）
                ocrFile.setCompanyName(wordsResult.getString("PurchaserName"));
                //银行回执单识别调用
            }else if (bankReceiptUrl.equals(url)){
                // TO DO
            }
            //文件url
            ocrFile.setUrl(fileUrl);
            //存储到list
            list.add(ocrFile);
            successNum++;
            successMsg.append("<br/>" + successNum + "、文件名 " + fileName + " 识别成功");
        }
        map.put("successNum", successNum);
        map.put("successMsg", successMsg);
        map.put("failureNum", failureNum);
        map.put("failureMsg", failureMsg);
        map.put("data", list);
        return map;
    }


    public static String getApiKey() {
        return apiKey;
    }

    public void setApiKey(String apiKey) {
        OcrUtil.apiKey = apiKey;
    }

    public static String getSecretKey() {
        return secretKey;
    }

    public void setSecretKey(String secretKey) {
        OcrUtil.secretKey = secretKey;
    }

    public static String getInvoiceUrl() {
        return invoiceUrl;
    }

    public void setInvoiceUrl(String invoiceUrl) {
        OcrUtil.invoiceUrl = invoiceUrl;
    }

    public static String getTokenUrl() {
        return tokenUrl;
    }

    public void setTokenUrl(String tokenUrl) {
        OcrUtil.tokenUrl = tokenUrl;
    }

    public static String getBankReceiptUrl() {
        return bankReceiptUrl;
    }

    public  void setBankReceiptUrl(String bankReceiptUrl) {
        OcrUtil.bankReceiptUrl = bankReceiptUrl;
    }
}
